#!/usr/bin/python3
# ******************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
"""
Description: modify commands entrance
Class:
"""
import datetime
import os
import re

import requests
from retrying import retry

from javcra.api.gitee_api import Issue
from javcra.common import constant
from javcra.libs.log import logger
from javcra.libs.read_excel import download_file


class Operation(Issue):

    def init_md_table(self, t_head=None, body_info=None, block_title="", prefix="", suffix=""):
        """
        initialize the md table for release issue

        Args:
            t_head: table head. e.g.["CVE", "仓库", "status"]
            body_info: table body info
            block_title: title of block. e.g.: "## 1.CVE"
            prefix: table prefix. e.g.: "修复cve xx 个"
            suffix: characters between the end of the table and the next block.

        Raises:
            ValueError: The thead must be a list or tuple

        Returns:
            str: markdown table str
        """
        if not t_head:
            t_head = []

        if not isinstance(t_head, (list, tuple)):
            raise ValueError("The thead must be a list or tuple.")

        thead_str = "|" + "|".join(t_head) + "|\n" + "|-" * len(t_head) + "|\n"
        tbody_str = self.convert_md_table_format(t_head, body_info)
        table_str = thead_str + tbody_str

        if prefix:
            table_str = prefix + "\n" + table_str
        return "\n".join([block_title, table_str, suffix])

    @staticmethod
    def convert_md_table_format(table_head, issue_info):
        """
        get markdown table body according to table_head and issue_info

        Args:
            table_head: table head like ["issue","status",...]
            issue_info: issue info like [{"issue":...,"status":...},....]

        Returns:
            markdown table str
        """
        table_body_str = ""
        if not issue_info:
            issue_info = []

        for info in issue_info:
            table_body_str += "|"
            for word in table_head:
                table_body_str += str(info.get(word)) + "|"

            table_body_str += "\n"
        return table_body_str

    @staticmethod
    def get_block_lines(issue_body_lines, start_flag, end_flag):
        """
        get block lines of specific part from issue body lines
        Args:
            issue_body_lines: the lines of issue body
            start_flag: start flag of specific part, like ""## 1、CVE""
            end_flag: end flag of specific part, like "\n"

        Returns: block_lines: lines in specific part like "cve part"
                 block_start_idx: start index of specific part
                 block_end_idx: end index of specific part

        """
        block_lines = []
        block_start_idx = 0
        block_end_idx = 0
        flag = 0

        # get block lines
        for idx, line in enumerate(issue_body_lines):
            if line.startswith(start_flag):
                # represents the start of block
                flag = 1
                block_start_idx = idx
                block_lines.append(line)
                continue

            if flag and line == end_flag:
                block_end_idx = idx
                break

            if flag:
                block_lines.append(line)

        return block_lines, block_start_idx, block_end_idx

    @staticmethod
    def modify_block_lines(origin_lines, block_lines, block_start, block_end):
        """
            modify block lines for add or delete operation
        Args:
            origin_lines: list, issue body splitlines
            block_lines: list, block str splitlines
            block_start: int, start index of block
            block_end: int, end index of block

        Returns:
            new lines for issue body, list
        """
        # to get count and then modify str "修复CVE xxx个"
        fix_line_idx = -1
        count = 0
        for index, cur_ln in enumerate(block_lines):
            if cur_ln.startswith("修复"):
                fix_line_idx = index
            if cur_ln.startswith("|#"):
                count += 1

        if fix_line_idx != -1:
            block_lines[fix_line_idx] = re.sub(
                "\d+", str(count), block_lines[fix_line_idx]
            )

        # modify block lines
        origin_lines[block_start:block_end] = block_lines
        return origin_lines

    @staticmethod
    def __append_info_in_specific_block(append_info, block_lines):
        """
        append info in specific block for add operation
        Args:
            append_info: issue info or requires info, dict
            block_lines: lines of specific block

        Returns:
                block_lines: block lines after append
        """

        for key, value in append_info.items():
            # if the issue to be added is already in the table, then continue
            if any([key in line for line in block_lines]):
                logger.warning("issue {} already exists in body content.".format(key))
                continue

            # if the requires info to be added already in the table, then not add
            value_lines = value.splitlines(keepends=True)
            append_value_lines = []
            for line in value_lines:
                if line not in block_lines:
                    append_value_lines.append(line)
            value = "".join(append_value_lines)
            block_lines.append(value)

        return block_lines

    @staticmethod
    def __delete_issue_in_specific_block(delete_issue, block_lines):
        """
        delete issue in specific block for delete operation
        Args:
            block_lines: lines of specific block
            delete_issue: issue to delete

        Returns:
            block_lines: block lines after delete
        """
        to_remove_idx = -1
        for idx, block_line in enumerate(block_lines):
            if delete_issue in block_line:
                to_remove_idx = idx
                break

        if to_remove_idx != -1:
            block_lines.pop(to_remove_idx)
        else:
            logger.warning("The specified issue {} does not exist in release issue.".format(delete_issue))
        return block_lines

    @staticmethod
    def __update_info_in_specific_block(update_info, block_lines):
        """
        update info in specific block for update operation
        Args:
            update_info:
            block_lines:

        Returns:

        """
        for key, value in update_info.items():
            if not value:
                continue
            for idx, ln in enumerate(block_lines):
                if any([key in ln]):
                    block_lines.pop(idx)
                    block_lines.append(value)
        return block_lines

    def get_new_body_lines(self, old_issue_body, append_info=None, update_info=None, delete_issue=None,
                           start_flag="", end_flag="\n"):
        """
        generating a new issue body by add and delete operation

        Args:
            old_issue_body: old issue body
            append_info: issues to add. like {issue_id:{"repo":..,"status":...},...}. Defaults to None.
            delete_issue: issues to delete. Defaults to None.
            start_flag: start flag of block Defaults to "".
            end_flag: end flag of block. Defaults to "\n".

        Raises:
            ValueError: [description]

        Returns:
            [type]: [description]
        """
        if not any((append_info, update_info, delete_issue)):
            raise ValueError("append_info、 delete_info or update info need at least one")

        lines = old_issue_body.splitlines(keepends=True)
        block_lines, block_start_idx, block_end_idx = self.get_block_lines(lines, start_flag, end_flag)

        if append_info:
            block_lines = self.__append_info_in_specific_block(append_info, block_lines)
        elif delete_issue:
            block_lines = self.__delete_issue_in_specific_block(delete_issue, block_lines)
        else:
            block_lines = self.__update_info_in_specific_block(update_info, block_lines)

        final_lines = self.modify_block_lines(lines, block_lines, block_start_idx, block_end_idx)
        return "".join(final_lines)

    def add_for_specific_block(self, body_str, issues, table_head, block_name):
        """
        add info in specific block

        Args:
            body_str: str, issue body
            issues: issues to be add
            table_head: list, table head
            block_name: block name

        Returns:
            processed issue body str
        """

        if not body_str:
            raise ValueError("no content of release issue, failed to add, start update first.")

        issues_dict = dict()
        issues_info_list = []
        print("block_name:", block_name)

        if "requires" in block_name:
            requires_md_str = self.convert_md_table_format(table_head, issues)
            if requires_md_str:
                issues_info_list.append(requires_md_str)
            issues_dict = {"requires_str": requires_md_str}
        else:
            for issue_id in issues:
                # get detail info of each issue to be add
                single_issue_info = self.get_single_issue_info(issue_id, block_name)
                if single_issue_info:
                    issues_info_list.append(single_issue_info)
                    issue_info = self.convert_md_table_format(table_head, single_issue_info)
                    issues_dict.setdefault(issue_id, issue_info)

        # if all the info to be add are empty
        if not issues_info_list:
            raise ValueError("failed to add, please check whether the issues to be added exists.")

        return self.get_new_body_lines(
            body_str, append_info=issues_dict, start_flag=block_name, end_flag="\n"
        )

    def delete_for_specific_block(self, body_str, issues, block_name):
        """
        delete info in specific block
        Args:
            body_str: str, issue body
            issues: issues to be delete
            block_name:block name

        Returns:
            processed issue body str
        """
        if not body_str:
            raise ValueError("no content of release issue, failed to delete, start update first.")

        res_str = body_str
        for issue_id in issues:
            res_str = self.get_new_body_lines(
                res_str, delete_issue=issue_id, start_flag=block_name, end_flag="\n"
            )
        return res_str

    def update_for_specific_block(self, body_str, issues, table_head, block_name):
        """

        Args:
            body_str:
            issues:
            table_head:
            block_name:

        Returns:

        """
        if not body_str:
            raise ValueError("no content of release issue, failed to update, start update first..")
        to_update = {}
        for issue_id in issues:
            single_issue_info = self.get_single_issue_info(issue_id, block_name)
            to_update.setdefault(
                issue_id, self.convert_md_table_format(table_head, single_issue_info)
            )

        return self.get_new_body_lines(
            body_str, update_info=to_update, start_flag=block_name, end_flag="\n"
        )

    def operate_for_specific_block(self, table_head, block_name, table_body=None, prefix="", operate="init",
                                   body_str=None, issues=None):
        """
        Process init, add, delete operations for specific block

        Args:
            table_head: list, table head
            block_name: str, block name like ""## 1、CVE""
            table_body: table_body of specific part for init, like [{..},{..},..].
            prefix: prefix of block, like "修复了bugfix xxx个"
            operate: init, add, delete
            body_str: issue body, str
            issues: issue id, list

        Raises:
            ValueError: not allowed operate

        Returns:
            str: processed issue body str
        """
        if not table_body:
            table_body = []

        if operate == "init":
            return self.init_md_table(table_head, table_body, block_name, prefix)
        elif operate == "add":
            return self.add_for_specific_block(body_str, issues, table_head, block_name)
        elif operate == "delete":
            return self.delete_for_specific_block(body_str, issues, block_name)
        elif operate == "update":
            return self.update_for_specific_block(body_str, issues, table_head, block_name)
        else:
            raise ValueError(
                "not allowed 'operate' value,expected in ['init','add','delete'],but given {}".format(operate)
            )

    @staticmethod
    def get_score(body_str):
        """
        get the score of cve

        Args:
            body_str: cve issue body str

        Returns:
            str: score value or no score
        """
        base_score_pattern = re.compile("BaseScore[：|:](?P<base_score>.*)")
        euler_score_pattern = re.compile(
            "openEuler评分.*?(?P<euler_score>[0-9\.]+)", flags=re.S
        )
        euler_res = euler_score_pattern.search(body_str)
        base_score = base_score_pattern.search(body_str)

        if euler_res:
            return euler_res["euler_score"]
        elif base_score:
            return base_score["base_score"]
        else:
            return "no score info"

    def is_abi_change(self, body_str):
        """
        Parsing whether the abi has changed

        Args:
            body_str: cve issue body

        Returns:
            "是" or "否"
        """
        branch = self.get_update_issue_branch()
        if not branch:
            return "否"

        abi_content_pattern = re.compile("修复是否涉及abi变化.*?(?P<abi>.*)[\\n$]", flags=re.S)
        abi_res = abi_content_pattern.search(body_str)

        if not abi_res:
            logger.error("abi pattern does not match the info")
            return "否"

        abi_branch = abi_res["abi"]
        for line in abi_branch.splitlines():
            if branch in line and "是" in line:
                return "是"
        return "否"

    def get_single_issue_info(self, issue_id, block_name):
        """
        get singe issue info for bugfix block

        Args:
            block_name: name of block
            issue_id: issue id

        Returns:
            list: bugfix info list
        """
        issue_content = self.get_issue_info(issue_number=issue_id)
        if not issue_content:
            logger.error("can not get the content of issue {}, perhaps this issue does not exist.".format(issue_id))
            return []

        issue_body = self.get_issue_body(issue_id)
        if not issue_body:
            logger.error("empty issue body for {}, can not get the info for {} block.".format(issue_id, block_name))
            return []

        repository = issue_content.get("repository", {})
        issue_info = {
            "仓库": repository.get("name", "无仓库信息"),
            "status": issue_content.get("issue_state", "无状态信息"),
        }

        block_names_list = ["## 2、bugfix", "# 3、安装、自编译问题", "# 4、遗留问题"]
        if block_name in block_names_list:
            issue_info["issue"] = "#" + issue_id
            issue_info["status"] = issue_content.get("issue_state", "无状态信息")

        if "遗留" in block_name:
            issue_info["type"] = issue_content.get("issue_type", "无type信息")
            issue_info["status"] = "遗留"

        if "CVE" in block_name or "cve" in block_name:
            version_pattern = re.compile("漏洞归属的版本[：:](?P<version>.*)")
            version = version_pattern.search(issue_body)
            issue_info["CVE"] = "#" + issue_id
            issue_info["score"] = self.get_score(issue_body)
            issue_info["version"] = version["version"] if version else "no version info"
            issue_info["abi是否变化"] = self.is_abi_change(issue_body)

        return [issue_info]

    def init(self):
        """init specific block

        Returns:
            str: init str
        """
        return self.get_new_issue_body(operate="init")

    def get_new_issue_body(self, *args, **kwargs):
        raise NotImplementedError


class CveIssue(Operation):

    def __init__(self, repo, token, issue_num):
        super().__init__(repo, token, issue_num)

    def create_cve_list(self, user_email):
        """
        The CVE-Manager is triggered to generate the CVE list and archive it
        Args:
            user_email (str): gitee user email
        """
        # Take cVE within three months
        start_time = (datetime.datetime.now() + datetime.timedelta(days=-90)).strftime('%Y-%m-%d')
        email_name = user_email.split('@')[0]
        url = "https://api.openeuler.org/cve-manager/v1/download/excel/triggerCveData?startTime=" + \
              start_time + "&typeName=" + email_name
        try:
            response = requests.get(url, headers=self.headers)
            if response.status_code == 200:
                logger.info("The CVE-Manager is triggered to generate the CVE list and archive the cVE list")
                return True
            logger.error("The CVE List file fails to be archived")
            return False
        except requests.RequestException as error:
            logger.error("The CVE List file fails to be archived because %s " % error)
            return False

    def get_cve_list(self):
        """
        Obtain cVE-related information provided by the CVE-Manager.
        Returns:
            cve_list: Data in Excel in dictionary form
        """

        @retry(stop_max_attempt_number=3, wait_fixed=1000)
        def create_list():

            res = self.create_cve_list(os.getenv("user_email"))
            if not res:
                raise ValueError("cve_manage archiving failed")

        @retry(stop_max_attempt_number=5, wait_fixed=60000)
        def get_list():
            now_time = datetime.date(
                datetime.date.today().year,
                datetime.date.today().month,
                datetime.date.today().day,
            ).strftime("%Y-%m-%d")
            branch_name = self.get_update_issue_branch()
            if not branch_name:
                logger.error("Failed to obtain branch")
                return []
            cve_list = download_file(now_time, "{}_updateinfo.xlsx".format(branch_name))
            if not cve_list:
                logger.error("Failed to obtain CVE data")
                raise ValueError("Failed to obtain CVE data")
            return cve_list

        create_list()
        cve_list = get_list()
        return cve_list

    def get_new_issue_body(self, operate="init", body_str=None, issues=None):
        """
        get new issue body for cve block operation

        Args:
            operate: operate str. Defaults to "init".expected [init,add,delete]
            body_str: gitee issue body str.
            issues: issue id list.

        Returns:
            new issue body str
        """
        if not issues:
            issues = []

        t_head = ["CVE", "仓库", "status", "score", "version", "abi是否变化"]
        block_name = "## 1、CVE"
        cve_list = [] if operate != "init" else self.get_cve_list()
        print(cve_list)
        cve_prefix = "修复CVE {}个".format(len(cve_list))

        return self.operate_for_specific_block(t_head, block_name, prefix=cve_prefix, operate=operate,
                                               table_body=cve_list, body_str=body_str, issues=issues)


class BugFixIssue(Operation):
    def __init__(self, repo, token, issue_num):
        super().__init__(repo, token, issue_num)

    def get_new_issue_body(self, operate="init", body_str=None, issues=None):
        """
        get new issue body for bugfix block operation

        Args:
            operate: operate str. Defaults to "init".expected [init,add,delete]
            body_str: gitee issue body str.
            issues: issue id list.

        Returns:
            str: new issue body str
        """
        if not issues:
            issues = []

        table_head = ["issue", "仓库", "status"]
        block_name = "## 2、bugfix"
        bugfix_list = []
        bugfix_prefix = "修复bugfix {}个".format(len(bugfix_list))

        return self.operate_for_specific_block(
            table_head,
            block_name,
            prefix=bugfix_prefix,
            operate=operate,
            table_body=bugfix_list,
            body_str=body_str,
            issues=issues,
        )


class RequiresIssue(Operation):
    def __init__(self, repo, token, issue_num):
        super().__init__(repo, token, issue_num)

    @staticmethod
    def get_requires_list():
        """
        get requires list

        Returns:
            requires list, like [{"仓库":..., "引入原因":...},...]
        """
        # since the code that generates pkg requires is not in the repository,
        # so it is assumed that the return value is []
        return []

    def get_new_issue_body(self, operate="init", body_str=None, issues=None):
        """
        get new issue body for requires block operation

        Args:
            operate. Defaults to "init".expected [init,add,delete]
            body_str: gitee issue body str.
            issues: issue id list.

        Returns:
            new issue body str
        """

        t_head = ["仓库", "引入原因"]
        block_name = "## 3、requires"

        if operate not in ["init", "add"]:
            raise ValueError("requires block operation only allowed in ['init', 'add'].")

        return self.operate_for_specific_block(
            t_head, block_name, operate=operate, body_str=body_str, issues=self.get_requires_list()
        )


class InstallBuildIssue(Operation):
    def __init__(self, repo, token, issue_num):
        super().__init__(repo, token, issue_num)

    def get_new_issue_body(self, operate="init", body_str=None, issues=None):
        """
        get new issue body for install build operation

        Args:
            operate: operate str. Defaults to "init".expected [init,add,delete]
            body_str: gitee issue body str.
            issues: issue id list.

        Returns:
            new issue body str
        """
        table_head = ["issue", "仓库", "status"]
        block_name = "# 3、安装、自编译问题"

        return self.operate_for_specific_block(
            table_head, block_name, operate=operate, body_str=body_str, issues=issues
        )


class RemainIssue(Operation):
    def __init__(self, repo, token, issue_num):
        super().__init__(repo, token, issue_num)

    def get_new_issue_body(self, operate="init", body_str=None, issues=None):
        """
        get new issue body for remain block operation

        Args:
            operate (str, optional): operate str. Defaults to "init".expected [init,add,delete]
            body_str (str, optional): gitee issue body str. Defaults to None.
            issues (list, optional): issue id list. Defaults to None.

        Returns:
            str: new issue body str
        """
        t_header = ["issue", "仓库", "status", "type"]
        block_name = "# 4、遗留问题"
        return self.operate_for_specific_block(
            t_header, block_name, operate=operate, body_str=body_str, issues=issues
        )


class IssueOperation(Operation):
    def __init__(self, repo, token, issue_num):
        super().__init__(repo, token, issue_num)
        args = (repo, token, issue_num)
        self.cve_object = CveIssue(*args)
        self.bugfix_object = BugFixIssue(*args)
        self.requires_object = RequiresIssue(*args)
        self.install_build_object = InstallBuildIssue(*args)
        self.remain_object = RemainIssue(*args)

    def init_repo_table(self):
        block_name = "# 2、测试repo源"
        table_head = ["repo_type", "architecture", "url"]
        table_str = self.init_md_table(table_head)
        return block_name + table_str

    def _get_repo(self, table_type="md"):
        """
        get repo according to branch 、date and epol
        """
        branch = self.get_update_issue_branch()
        if not branch:
            raise ValueError("can not get the branch, please check.")

        base_url = "http://121.36.84.172/repo.openeuler.org/" + branch

        repos = []
        archs = ["aarch64", "x86_64"]
        for arch in archs:
            repo_dict = dict()
            repo_dict["repo_type"] = "standard"
            repo_dict["architecture"] = arch
            repo_dict["url"] = base_url + "/update_" + self.date + "/" + arch + "/"
            repos.append(repo_dict)

        pkglist = self.get_update_list()
        _, epol_list = self.get_standard_epol_list(branch, pkglist)
        if epol_list:
            for arch in archs:
                repo_dict = dict()
                repo_dict["repo_type"] = "epol"
                repo_dict["architecture"] = arch
                if "sp2" in branch or "SP2" in branch:
                    repo_dict["url"] = base_url + "/EPOL/update_" + self.date + "/main/" + arch + "/"
                else:
                    repo_dict["url"] = base_url + "/EPOL/update_" + self.date + "/" + arch + "/"
                repos.append(repo_dict)

        if table_type == "md":
            t_header = ["repo_type", "architecture", "url"]
            block_name = "# 2、测试repo源"
            return self.init_md_table(t_head=t_header, body_info=repos, block_title=block_name)
        else:
            return repos

    def create_jenkins_comment(self, jenkins_result):
        """method to create issue comment

        Args:
            comment (str): comment strdef test(self):
        """
        th = ["name", "status", "output"]
        comment = self.init_md_table(th, jenkins_result)
        comment_res = self.create_issue_comment(comment)
        return comment_res

    def create_install_build_issue(self, failed_type, pkg_name):
        branch = self.get_update_issue_branch()
        if not branch:
            logger.error("failed to create install build issue, owning to not found release issue branch.")
            return None

        params = dict()
        params["repo"] = pkg_name
        params["owner"] = self.owner
        params["access_token"] = self.token
        params["title"] = "[{brh}] {pkg} {verify_type} failed".format(
            pkg=pkg_name, verify_type=failed_type, brh=branch
        )

        command = ""
        if failed_type == "build":
            command = "rpmbuild --rebuild"
        if failed_type == "install":
            command = "yum install"

        params["body"] = """Branch: {brh}
                   Component: {pkg}
                   Instructions to reappear the problem : {command}
                   Expected results: successfully {_type}
                   Actual results: failed {_type}""".format(brh=branch, pkg=pkg_name, command=command,
                                                            _type=failed_type)
        create_res = self.create_issue(params)
        return create_res

    def _get_update_version_info(self):
        """获取版本目标 和版本负责人那两段文字的方法

        Returns:
            str: 版本目标和版本负责人
        """
        issue_body = self.get_issue_body(self.issue_num)
        if issue_body:
            if re.compile("1、CVE.*?\\n\\n", re.S).search(issue_body):
                print("Issue has CVE content, have you already operated start update command")
                return None
            if not issue_body.endswith("\n"):
                issue_body += "\n"
            return issue_body
        else:
            return None

    def _init_issue_description(self):
        """
        initialize the release issue body when commenting "start-update" command

        Returns:
            True or False
        """
        update_info = self._get_update_version_info()
        if not update_info:
            return
        release_range = "# 1、发布范围\n"
        cve_block_str = self.cve_object.init()
        bugfix_block_str = self.bugfix_object.init()
        requires_block_str = self.requires_object.init()
        repo_block_str = self.init_repo_table()
        install_build_block_str = self.install_build_object.init()
        remain_block_str = self.remain_object.init()

        body_str = (
                update_info
                + release_range
                + cve_block_str
                + bugfix_block_str
                + requires_block_str
                + repo_block_str
                + install_build_block_str
                + remain_block_str
        )

        print(body_str)
        return True if self.update_issue(body=body_str) else False

    def _process_issue_id(self, body):
        """处理md字符串,获取issue id, 少巍写的函数

        Args:
            body (str): block body
            block_name (str): given [cve,bugfix,req]

        Returns:
            set: current block repos
        """
        content = re.compile("#[a-zA-Z0-9]+", re.S).findall(body)
        if not content:
            return content
        return [con.replace("#", "") for con in content]

    def _get_install_build_bugfix_issue_id(self, issue_body: str):
        """正则获取对应block元素,

        Args:
            issue_body (str): issue body str

        Returns:
            list: repos list
        """
        install_build_issues = set()
        bugfix_issues = set()
        cve_issues = set()
        install_build_issue_list = []
        bugfix_issue_list = []
        cve_issue_list = []

        install_build_res = re.compile("(?P<install_build>3、安装、自编译问题.*?\\n\\n)",
                                       re.S).search(issue_body)
        bugfix_res = re.compile(
            "(?P<bugfix>2、bugfix.*?\\n\\n)", re.S).search(issue_body)
        cve_res = re.compile(
            "(?P<cve>1、CVE.*?\\n\\n)", re.S).search(issue_body)
        if install_build_res:
            install_build_issue_list = self._process_issue_id(install_build_res["install_build"])
            install_build_issues.update(install_build_issue_list)
        else:
            logger.error("re not find install build content,pattern is {}".format(install_build_res.re))
        if bugfix_res:
            bugfix_issue_list = self._process_issue_id(bugfix_res["bugfix"])
            bugfix_issues.update(bugfix_issue_list)
        else:
            logger.error("re not find bugfix content,pattern is {}".format(bugfix_res.re))

        if cve_res:
            cve_issue_list = self._process_issue_id(cve_res["cve"])
            cve_issues.update(cve_issue_list)
        else:
            logger.error("re not find cve content,pattern is {}".format(bugfix_res.re))
        if len(install_build_issue_list) == 0 and len(bugfix_issue_list) == 0 and len(cve_issue_list) == 0:
            logger.error("Block has no related issues")
        return list(install_build_issues), list(bugfix_issues), list(cve_issues)

    def check_issue_state(self):
        """
       更新install_build 的issue及转测的 issue状态, 少巍写的函数
       Args:
           issues_id:

       Returns:

       """
        # TODO: 请改成这个获取openeuerl下的issue信息 issue_content = self.get_issue_info(self.issue_num)
        try:
            body = self.get_issue_body(self.issue_num)
            install_build_issues, bugfix_issues, _ = self._get_install_build_bugfix_issue_id(body)
            if install_build_issues:
                self.operate_release_issue(operation="update", operate_block="install_build",
                                           issues=install_build_issues)
            if bugfix_issues:
                self.operate_release_issue(operation="update", operate_block="bugfix", issues=bugfix_issues)
        except ValueError as e:
            logger.error(e)
            return False
        return True

    def update_remain_issue_state(self, issue_list, action):
        """
       更新遗留的 issue状态
       Args:
           issue_list: issues
           action: add or delete
       Returns:

       """
        update_flag = True
        try:
            issue_content = self.get_issue_info(self.issue_num)
            body = issue_content.get("body")
            _, bugfix_issues, cve_issue = self._get_install_build_bugfix_issue_id(body)
            to_update = {}
            for issue in issue_list:
                if issue in bugfix_issues:
                    t_head = ["issue", "仓库", "status"]
                    block_name = '## 2、bugfix'
                    operate_ins = getattr(self, "bugfix" + "_object")
                    new_con = operate_ins.get_single_issue_info(issue, block_name)[0]
                elif issue in cve_issue:
                    t_head = ["CVE", "仓库", "status", "score", "version", "abi是否变化"]
                    operate_ins = getattr(self, "cve" + "_object")
                    block_name = '## 1、CVE'
                    new_con = operate_ins.get_single_issue_info(issue, block_name)[0]

                else:
                    logger.warning("issue {} not exist in cve and bugfix part.".format(issue))
                    update_flag = False
                    break
                if action == "add":
                    new_con["status"] = "遗留"
                to_update.setdefault(
                    issue, self.convert_md_table_format(t_head, [new_con])
                )
                body_str = self.get_new_body_lines(
                    body, update_info=to_update, start_flag=block_name, end_flag="\n"
                )
                self.update_issue(body=body_str)
        except (ValueError, AttributeError, IndexError) as e:
            logger.error(e)
            return False
        return update_flag

    def add_repo_in_table(self):
        start_flag = "# 2、测试repo源"
        end_flag = "\n"
        issue_body = self.get_issue_body(self.issue_num)
        if not issue_body:
            logger.error("can not get release issue body, failed to add repo in tabe.")
            return False

        issue_body_lines = issue_body.splitlines(keepends=True)

        try:
            repo_lines, start_idx, end_idx = self.get_block_lines(issue_body_lines, start_flag, end_flag)
            new_repo_lines = self._get_repo().splitlines(keepends=True)
            final_lines = self.modify_block_lines(issue_body_lines, new_repo_lines, start_idx, end_idx)
            body_str = "".join(final_lines)
            return True if self.update_issue(body=body_str) else False
        except ValueError as err:
            logger.error(err)
            return False

    def _update_issue_description(self, operate="", update_block="", issues=None):
        """处理add delete 的方法入口

        Args:
            operate (str, optional): 操作方式 如{init,add,delete}. Defaults to "".
            update_block (str, optional): 操作的块元素,如cve bugfix,remain... Defaults to "".
            issues ([type], optional): issue 列表. Defaults to None.

        Raises:
            ValueError: Unable to get the content of issue, please check!
            ValueError: The current issue has no content, please start first!!
            ValueError: No content retrieved, please check!!
        """
        old_body_str = self.get_issue_body(self.issue_num)
        if not old_body_str:
            logger.error("The current issue has no content, please start first.")
            return False

        operate_object = getattr(self, update_block + "_object")  # 获取类的实例
        body_str = operate_object.get_new_issue_body(
            operate=operate, body_str=old_body_str, issues=issues)

        if not body_str:
            logger.error("No content retrieved, please check.happy")
            return False

        print(body_str)
        return True if self.update_issue(body=body_str) else False

    def operate_release_issue(self, operation="init", operate_block=None, issues=None):
        """类调用入口

        Args:
            operation (str, optional): 操作方式. Defaults to "init",在{init,add,delete}中选择.
            operate_block (str, optional): [description]. Defaults to None,
                    在{cve,bugfix,install_build,test,remain}中,当operate是init的时候可以不传.
            issues ([type], optional): [description]. Defaults to None , issue列表.

        Raises:
            ValueError: must given operation!!! and please given ['init','add','delete']
            ValueError: if operate not 'init',must given operate_block

        Returns:
            [type]: [description]
        """
        if not issues:
            issues = []

        valid_operations = ["init", "add", "delete", "update"]

        if not operation or operation not in valid_operations:
            logger.error("must given operation in ['init','add','delete']")

        try:
            if operation == "init":
                return self._init_issue_description()
            else:
                return self._update_issue_description(
                    operate=operation, update_block=operate_block, issues=issues
                )
        except ValueError as e:
            logger.error(e)
            return False


def test_main():
    _repo = "release-tools"
    # owner = "tushenmei"
    token = constant.GITEE_TOKEN
    issue_num = "I42F0Y"

    # TODO:更多入口请从命令行解决 当前这个只是实现类的地方
    s = IssueOperation(_repo, token, issue_num)
    # res = s.people_review()
    # print(res)

    # 这个是对应start的时候.
    # res = s.operate_release_issue(operation="init")
    # print(res)

    # 更新repo
    # res = s.add_repo_in_table()
    # print(res)

    # CVE
    # s.operate_release_issue(operation="add", operate_block="cve", issues=["I41R53", "I3V9IG"])
    res = s.operate_release_issue(operation="add", operate_block="cve", issues=["I41R53"])
    # res = s.operate_release_issue(operation="delete", operate_block="cve", issues=["I3OC6A"])
    print(res)

    # bugfix
    # res = s.operate_release_issue(operation="add",operate_block="bugfix",issues=["I3OC6A","I3V9IG"])
    # print(res)
    # s.operate_release_issue(operation="delete", operate_block="bugfix", issues=["I3OC6A"])

    # install_build
    # res = s.operate_release_issue(operation="add", operate_block="install_build", issues=["I3OC6A", "I3V9IG"])
    # print(res)
    # s.operate_release_issue(operation="delete", operate_block="install_build", issues=["I3OC6A"])

    # remain issue: I3AHLY
    s.operate_release_issue(operation="add", operate_block="remain", issues=["I3AHLY", "I3OC6A"])
    # res = s.operate_release_issue(operation="delete", operate_block="remain", issues=["I3AHLY"])

    # 通过get_requires 命令触发,注意这个触发的只有add
    # res = s.operate_release_issue(operation="add", operate_block="requires")
    # print(res)

    # 获取pkglist
    # pkg_list = s.get_update_list()
    # print(pkg_list)

    # 创建自编译或安装失败的issue
    # res = s.create_install_build_issue("build", "bind")
    # print(res)
    #
    # res = s.get_issue_info("I3OC6A")
    # print(res)

    # 更新遗留状态
    # issue_list = ["I3VI8Y"]
    # check_res = s.update_remain_issue_state(issue_list)
    # print(check_res)


if __name__ == "__main__":
    test_main()
